package com.idanchuang.component.redis.util;

import com.idanchuang.component.core.util.SpringUtil;
import com.idanchuang.component.core.config.EnvConfigBean;
import com.idanchuang.component.core.helper.EnvContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.RedisScript;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Redis操作帮助类
 *
 * 有两种方式可以使用它!
 *
 * 1: 这种方式只能在被Spring管理的Component类上使用, 且此类必须能被Spring扫描到, 否则无法完成注入
 * <pre>
 *     \@Autowired(required = false)
 *     private RedisUtil redisUtil;
 *
 *     ...
 *     redisUtil.set(key, value);
 *     redisUtil.setObj(key, value);
 *     ...
 * </pre>
 *
 * 2: 无限制
 * <pre>
 *     ...
 *     RedisUtil redisUtil = RedisUtil.getInstance();
 *     redisUtil.set(key, value);
 *     redisUtil.setObj(key, value);
 *     ...
 * </pre>
 *
 * @author yjy
 * 2018-05-19 19:54
 */
public class RedisUtil {

    protected final StringRedisTemplate strTemplate;
    protected final RedisTemplate<Object, Object> objTemplate;
    protected final ValueOperations<String, String> strVal;
    protected final ValueOperations<Object, Object> objVal;
    protected final HashOperations<String, String, String> strHash;
    protected final HashOperations<Object, Object, Object> objHash;
    protected final ListOperations<String, String> strList;
    protected final ListOperations<Object, Object> objList;
    protected final SetOperations<String, String> strSet;
    protected final SetOperations<Object, Object> objSet;
    protected final ZSetOperations<String, String> strZSet;
    protected final ZSetOperations<Object, Object> objZSet;

    @Autowired
    private EnvConfigBean envConfigBean;

    public RedisUtil(StringRedisTemplate strTemplate, RedisTemplate<Object, Object> objTemplate) {
        this.strTemplate = strTemplate;
        this.objTemplate = objTemplate;
        this.strVal = strTemplate.opsForValue();
        this.objVal = objTemplate.opsForValue();
        this.strHash = strTemplate.opsForHash();
        this.objHash = objTemplate.opsForHash();
        this.strList = strTemplate.opsForList();
        this.objList = objTemplate.opsForList();
        this.strSet = strTemplate.opsForSet();
        this.objSet = objTemplate.opsForSet();
        this.strZSet = strTemplate.opsForZSet();
        this.objZSet = objTemplate.opsForZSet();
    }

    private static volatile RedisUtil instance = null;

    @SuppressWarnings("unchecked")
    public static RedisUtil getInstance() {
        if (instance == null) {
            synchronized (RedisUtil.class) {
                if (instance == null) {
                    instance = SpringUtil.getBean(RedisUtil.class);
                }
            }
        }
        return instance;
    }

    public void set(String key, String val) {
        key = resolveKey(key);
        this.strVal.set(key, val);
        expireIfPressure(key);
    }

    public void setObj(Object key, Object val) {
        key = resolveObjKey(key);
        this.objVal.set(key, val);
        expireObjIfPressure(key);
    }

    public void set(String key, String val, long expires) {
        key = resolveKey(key);
        this.strVal.set(key, val, expires, TimeUnit.MILLISECONDS);
        expireIfPressure(key, expires, TimeUnit.MILLISECONDS);
    }

    public void setObj(Object key, Object val, long expires) {
        key = resolveObjKey(key);
        this.objVal.set(key, val, expires, TimeUnit.MILLISECONDS);
        expireObjIfPressure(key, expires, TimeUnit.MILLISECONDS);
    }

    public void set(String key, String val, long expires, TimeUnit timeUnit) {
        key = resolveKey(key);
        this.strVal.set(key, val, expires, timeUnit);
        expireIfPressure(key, expires, timeUnit);
    }

    public void setObj(Object key, Object val, long expires, TimeUnit timeUnit) {
        key = resolveObjKey(key);
        this.objVal.set(key, val, expires, timeUnit);
        expireObjIfPressure(key, expires, timeUnit);
    }

    public void multiSet(Map<String, String> map) {
        map = resolveKeys(map);
        this.strVal.multiSet(map);
        expireIfPressures(map.keySet());
    }

    public void multiSetObj(Map<Object, Object> map) {
        map = resolveObjKeys(map);
        this.objVal.multiSet(map);
        expireObjIfPressures(map.keySet());
    }

    public void multiSet(Map<String, String> map, long expires, TimeUnit timeUnit) {
        map = resolveKeys(map);
        this.strVal.multiSet(map);
        Set<String> keySet = map.keySet();
        for (String k : keySet) {
            this.strTemplate.expire(k, expires, timeUnit);
        }
        expireIfPressures(keySet, expires, timeUnit);
    }

    public void multiSetObj(Map<Object, Object> map, long expires, TimeUnit timeUnit) {
        map = resolveObjKeys(map);
        this.objVal.multiSet(map);
        Set<Object> keySet = map.keySet();
        for (Object k : keySet) {
            this.objTemplate.expire(k, expires, timeUnit);
        }
        expireObjIfPressures(keySet, expires, timeUnit);
    }

    public Boolean setIfAbsent(String key, String val) {
        key = resolveKey(key);
        Boolean res = this.strVal.setIfAbsent(key, val);
        expireIfPressure(key);
        return res;
    }

    public Boolean setObjIfAbsent(Object key, Object val) {
        key = resolveObjKey(key);
        Boolean res = this.objVal.setIfAbsent(key, val);
        expireObjIfPressure(key);
        return res;
    }

    public Boolean setIfAbsent(String key, String val, long expires, TimeUnit timeUnit) {
        key = resolveKey(key);
        Boolean res = this.strVal.setIfAbsent(key, val, expires, timeUnit);
        expireIfPressure(key, expires, timeUnit);
        return res;
    }

    public Boolean setObjIfAbsent(Object key, Object val, long expires, TimeUnit timeUnit) {
        key = resolveObjKey(key);
        Boolean res = this.objVal.setIfAbsent(key, val, expires, timeUnit);
        expireObjIfPressure(key, expires, timeUnit);
        return res;
    }

    public String get(String key) {
        key = resolveKey(key);
        return this.strVal.get(key);
    }

    public <T> T getObj(Object key) {
        key = resolveObjKey(key);
        return (T) this.objVal.get(key);
    }

    public String getAndSet(String key, String value) {
        key = resolveKey(key);
        String res = this.strVal.getAndSet(key, value);
        expireIfPressure(key);
        return res;
    }

    public <T> T getAndSetObj(Object key, Object value) {
        key = resolveObjKey(key);
        T res = (T) this.objVal.getAndSet(key, value);
        expireObjIfPressure(key);
        return res;
    }

    public List<String> multiGet(Collection<String> keys) {
        Collection<String> finalKeys = new ArrayList<>();
        for (String key : keys) {
            finalKeys.add(resolveKey(key));
        }
        return this.strVal.multiGet(finalKeys);
    }

    public List<Object> multiGetObj(Collection<Object> keys) {
        Collection<Object> finalKeys = new ArrayList<>();
        for (Object key : keys) {
            finalKeys.add(resolveObjKey(key));
        }
        return this.objVal.multiGet(finalKeys);
    }

    public Integer append(String key, String val) {
        key = resolveKey(key);
        Integer res = this.strVal.append(key, val);
        expireIfPressure(key);
        return res;
    }

    public Integer appendObj(Object key, String val) {
        key = resolveObjKey(key);
        Integer res = this.objVal.append(key, val);
        expireObjIfPressure(key);
        return res;
    }

    public Long increment(String key, long delta) {
        key = resolveKey(key);
        return this.strTemplate.opsForValue().increment(key, delta);
    }

    public void setList(String key, int index, String val) {
        key = resolveKey(key);
        this.strList.set(key, index, val);
        expireIfPressure(key);
    }

    public void setListObj(Object key, int index, Object val) {
        key = resolveObjKey(key);
        this.objList.set(key, index, val);
        expireObjIfPressure(key);
    }

    public String indexList(String key, int index) {
        key = resolveKey(key);
        return this.strList.index(key, index);
    }

    public Object indexListObj(Object key, int index) {
        key = resolveObjKey(key);
        return this.objList.index(key, index);
    }

    public Long pushList(String key, String... vals) {
        key = resolveKey(key);
        Long res = this.strList.rightPushAll(key, vals);
        expireIfPressure(key);
        return res;
    }

    public Long pushListObj(Object key, Object... vals) {
        key = resolveObjKey(key);
        Long res = this.objList.rightPushAll(key, vals);
        expireObjIfPressure(key);
        return res;
    }

    public Long addSet(String key, String... vals) {
        key = resolveKey(key);
        Long res = this.strSet.add(key, vals);
        expireIfPressure(key);
        return res;
    }

    public Long addSetObj(Object key, Object... vals) {
        key = resolveObjKey(key);
        Long res = this.objSet.add(key, vals);
        expireObjIfPressure(key);
        return res;
    }

    public String popSet(String key) {
        key = resolveKey(key);
        return this.strSet.pop(key);
    }

    public Object popSetObj(Object key) {
        key = resolveObjKey(key);
        return this.objSet.pop(key);
    }

    public List<String> popSet(String key, long count) {
        key = resolveKey(key);
        return this.strSet.pop(key, count);
    }

    public List<Object> popSetObj(Object key, long count) {
        key = resolveObjKey(key);
        return this.objSet.pop(key, count);
    }

    public Set<String> differenceSet(String key, String otherKey) {
        key = resolveKey(key);
        return this.strSet.difference(key, otherKey);
    }

    public Set<Object> differenceSetObj(Object key, Object otherKey) {
        key = resolveObjKey(key);
        return this.objSet.difference(key, otherKey);
    }

    public Set<String> differenceSet(String key, Collection<String> otherKey) {
        key = resolveKey(key);
        return this.strSet.difference(key, otherKey);
    }

    public Set<Object> differenceSetObj(Object key, Collection<Object> otherKey) {
        key = resolveObjKey(key);
        return this.objSet.difference(key, otherKey);
    }

    public Long differenceAndStoreSet(String key, String otherKey, String destKey) {
        key = resolveKey(key);
        return this.strSet.differenceAndStore(key, otherKey, destKey);
    }

    public Long differenceAndStoreSetObj(Object key, Object otherKey, Object destKey) {
        key = resolveObjKey(key);
        return this.objSet.differenceAndStore(key, otherKey, destKey);
    }

    public Long differenceAndStoreSet(String key, Collection<String> otherKey, String destKey) {
        key = resolveKey(key);
        return this.strSet.differenceAndStore(key, otherKey, destKey);
    }

    public Long differenceAndStoreSetObj(Object key, Collection<Object> otherKey, Object destKey) {
        key = resolveObjKey(key);
        return this.objSet.differenceAndStore(key, otherKey, destKey);
    }

    public Set<String> distinctRandomMembersSet(String key, long count) {
        key = resolveKey(key);
        return this.strSet.distinctRandomMembers(key, count);
    }

    public Set<Object> distinctRandomMembersSetObj(Object key, long count) {
        key = resolveObjKey(key);
        return this.objSet.distinctRandomMembers(key, count);
    }

    public Set<String> intersectSet(String key, String otherKey) {
        key = resolveKey(key);
        return this.strSet.intersect(key, otherKey);
    }

    public Set<Object> intersectSetObj(Object key, Object otherKey) {
        key = resolveObjKey(key);
        return this.objSet.intersect(key, otherKey);
    }

    public Set<String> intersectSet(String key, Collection<String> otherKey) {
        key = resolveKey(key);
        return this.strSet.intersect(key, otherKey);
    }

    public Set<Object> intersectSetObj(Object key, Collection<Object> otherKey) {
        key = resolveObjKey(key);
        return this.objSet.intersect(key, otherKey);
    }

    public Long intersectAndStoreSet(String key, String otherKey, String destKey) {
        key = resolveKey(key);
        return this.strSet.intersectAndStore(key, otherKey, destKey);
    }

    public Long intersectAndStoreSetObj(Object key, Object otherKey, Object destKey) {
        key = resolveObjKey(key);
        return this.objSet.intersectAndStore(key, otherKey, destKey);
    }

    public Long intersectAndStoreSet(String key, Collection<String> otherKey, String destKey) {
        key = resolveKey(key);
        return this.strSet.intersectAndStore(key, otherKey, destKey);
    }

    public Long intersectAndStoreSetObj(Object key, Collection<Object> otherKey, Object destKey) {
        key = resolveObjKey(key);
        return this.objSet.intersectAndStore(key, otherKey, destKey);
    }

    public Boolean isMemberSet(String key, String value) {
        key = resolveKey(key);
        return this.strSet.isMember(key, value);
    }

    public Boolean isMemberSetObj(Object key, Object value) {
        key = resolveObjKey(key);
        return this.objSet.isMember(key, value);
    }

    public Set<String> membersSet(String key) {
        key = resolveKey(key);
        return this.strSet.members(key);
    }

    public Set<Object> membersSetObj(Object key) {
        key = resolveObjKey(key);
        return this.objSet.members(key);
    }

    public Boolean moveSet(String key, String value, String destKey) {
        key = resolveKey(key);
        return this.strSet.move(key, value, destKey);
    }

    public Boolean moveSetObj(Object key, Object value, Object destKey) {
        key = resolveObjKey(key);
        return this.objSet.move(key, value, destKey);
    }

    public Long removeSet(String key, Object... values) {
        key = resolveKey(key);
        return this.strSet.remove(key, values);
    }

    public Long removeSetObj(Object key, Object... values) {
        key = resolveObjKey(key);
        return this.objSet.remove(key, values);
    }

    public String randomMemberSet(String key) {
        key = resolveKey(key);
        return this.strSet.randomMember(key);
    }

    public Object randomMemberSetObj(Object key) {
        key = resolveObjKey(key);
        return this.objSet.randomMember(key);
    }

    public List<String> randomMemberSet(String key, long count) {
        key = resolveKey(key);
        return this.strSet.randomMembers(key, count);
    }

    public List<Object> randomMemberSetObj(Object key, long count) {
        key = resolveObjKey(key);
        return this.objSet.randomMembers(key, count);
    }

    public Set<String> unionSet(String key, String otherKey) {
        key = resolveKey(key);
        return this.strSet.union(key, otherKey);
    }

    public Set<Object> unionSetObj(Object key, Object otherKey) {
        key = resolveObjKey(key);
        return this.objSet.union(key, otherKey);
    }

    public Set<String> unionSet(String key, Collection<String> otherKey) {
        key = resolveKey(key);
        return this.strSet.union(key, otherKey);
    }

    public Set<Object> unionSetObj(Object key, Collection<Object> otherKey) {
        key = resolveObjKey(key);
        return this.objSet.union(key, otherKey);
    }

    public Long unionAndStoreSet(String key, String otherKey, String destKey) {
        key = resolveKey(key);
        return this.strSet.unionAndStore(key, otherKey, destKey);
    }

    public Long unionAndStoreSetObj(Object key, Object otherKey, Object destKey) {
        key = resolveObjKey(key);
        return this.objSet.unionAndStore(key, otherKey, destKey);
    }

    public Long unionAndStoreSet(String key, Collection<String> otherKey, String destKey) {
        key = resolveKey(key);
        return this.strSet.unionAndStore(key, otherKey, destKey);
    }

    public Long unionAndStoreSetObj(Object key, Collection<Object> otherKey, Object destKey) {
        key = resolveObjKey(key);
        return this.objSet.unionAndStore(key, otherKey, destKey);
    }

    public Long sizeSet(String key) {
        key = resolveKey(key);
        return this.strSet.size(key);
    }

    public Long sizeSetObj(Object key) {
        key = resolveObjKey(key);
        return this.objSet.size(key);
    }

    public Set<String> rangeZSet(String key, long start, long end) {
        key = resolveKey(key);
        return this.strZSet.range(key, start, end);
    }

    public Set<Object> rangeZSetObj(Object key, long start, long end) {
        key = resolveObjKey(key);
        return this.objZSet.range(key, start, end);
    }

    public Set<String> rangeByLexZSet(String key, RedisZSetCommands.Range range) {
        key = resolveKey(key);
        return this.strZSet.rangeByLex(key, range);
    }

    public Set<Object> rangeByLexZSetObj(Object key, RedisZSetCommands.Range range) {
        key = resolveObjKey(key);
        return this.objZSet.rangeByLex(key, range);
    }

    public Set<String> rangeByLexZSet(String key, RedisZSetCommands.Range range, RedisZSetCommands.Limit limit) {
        key = resolveKey(key);
        return this.strZSet.rangeByLex(key, range, limit);
    }

    public Set<Object> rangeByLexZSetObj(Object key, RedisZSetCommands.Range range, RedisZSetCommands.Limit limit) {
        key = resolveObjKey(key);
        return this.objZSet.rangeByLex(key, range, limit);
    }

    public Boolean addZSet(String key, String value, double score) {
        key = resolveKey(key);
        Boolean res = this.strZSet.add(key, value, score);
        expireIfPressure(key);
        return res;
    }

    public Boolean addZSetObj(Object key, Object value, double score) {
        key = resolveObjKey(key);
        Boolean res = this.objZSet.add(key, value, score);
        expireObjIfPressure(key);
        return res;
    }

    public Long addZSet(String key, Set<ZSetOperations.TypedTuple<String>> tuples) {
        key = resolveKey(key);
        Long res = this.strZSet.add(key, tuples);
        expireIfPressure(key);
        return res;
    }

    public Long addZSetObj(Object key, Set<ZSetOperations.TypedTuple<Object>> tuples) {
        key = resolveObjKey(key);
        Long res = this.objZSet.add(key, tuples);
        expireObjIfPressure(key);
        return res;
    }

    public Set<String> rangeByScoreZSet(String key, double min, double max) {
        key = resolveKey(key);
        return this.strZSet.rangeByScore(key, min, max);
    }

    public Set<Object> rangeByScoreZSetObj(Object key, double min, double max) {
        key = resolveObjKey(key);
        return this.objZSet.rangeByScore(key, min, max);
    }

    public Set<String> rangeByScoreZSet(String key, double min, double max, long offset, long count) {
        key = resolveKey(key);
        return this.strZSet.rangeByScore(key, min, max, offset, count);
    }

    public Set<Object> rangeByScoreZSetObj(Object key, double min, double max, long offset, long count) {
        key = resolveObjKey(key);
        return this.objZSet.rangeByScore(key, min, max, offset, count);
    }

    public Long countZSet(String key, double min, double max) {
        key = resolveKey(key);
        return this.strZSet.count(key, min, max);
    }

    public Long countZSetObj(Object key, double min, double max) {
        key = resolveObjKey(key);
        return this.objZSet.count(key, min, max);
    }

    public Long rankZSet(String key, Object o) {
        key = resolveKey(key);
        return this.strZSet.rank(key, o);
    }

    public Long rankZSetObj(Object key, Object o) {
        key = resolveObjKey(key);
        return this.objZSet.rank(key, o);
    }

    public Long removeZSet(String key, Object... values) {
        key = resolveKey(key);
        return this.strZSet.remove(key, values);
    }

    public Long removeZSetObj(Object key, Object... values) {
        key = resolveObjKey(key);
        return this.objZSet.remove(key, values);
    }

    public Long sizeZSet(String key) {
        key = resolveKey(key);
        return this.strZSet.size(key);
    }

    public Long sizeZSetObj(Object key) {
        key = resolveObjKey(key);
        return this.objZSet.size(key);
    }

    public Double scoreZSet(String key, Object o) {
        key = resolveKey(key);
        return this.strZSet.score(key, o);
    }

    public Double scoreZSetObj(Object key, Object o) {
        key = resolveObjKey(key);
        return this.objZSet.score(key, o);
    }

    public Long zCardZSet(String key) {
        key = resolveKey(key);
        return this.strZSet.zCard(key);
    }

    public Long zCardZSetObj(Object key) {
        key = resolveObjKey(key);
        return this.objZSet.zCard(key);
    }

    public Long removeRangeZSet(String key, long start, long end) {
        key = resolveKey(key);
        return this.strZSet.removeRange(key, start, end);
    }

    public Long removeRangeZSetObj(Object key, long start, long end) {
        key = resolveObjKey(key);
        return this.objZSet.removeRange(key, start, end);
    }

    public Set<String> reverseRangeZSet(String key, long start, long end) {
        key = resolveKey(key);
        return this.strZSet.reverseRange(key, start, end);
    }

    public Set<Object> reverseRangeZSetObj(Object key, long start, long end) {
        key = resolveObjKey(key);
        return this.objZSet.reverseRange(key, start, end);
    }

    public Long reverseRankZSet(String key, Object o) {
        key = resolveKey(key);
        return this.strZSet.reverseRank(key, o);
    }

    public Long reverseRankZSetObj(Object key, Object o) {
        key = resolveObjKey(key);
        return this.objZSet.reverseRank(key, o);
    }

    public Long leftPushList(String key, String value) {
        key = resolveKey(key);
        Long res = this.strList.leftPush(key, value);
        expireIfPressure(key);
        return res;
    }

    public Long leftPushListObj(Object key, Object value) {
        key = resolveObjKey(key);
        Long res = this.objList.leftPush(key, value);
        expireObjIfPressure(key);
        return res;
    }

    public Long leftPushList(String key, String pivot, String value) {
        key = resolveKey(key);
        Long res = this.strList.leftPush(key, pivot, value);
        expireIfPressure(key);
        return res;
    }

    public Long leftPushListObj(Object key, Object pivot, Object value) {
        key = resolveObjKey(key);
        Long res = this.objList.leftPush(key, pivot, value);
        expireObjIfPressure(key);
        return res;
    }

    public Long leftPushAllList(String key, String... vals) {
        key = resolveKey(key);
        Long res = this.strList.leftPushAll(key, vals);
        expireIfPressure(key);
        return res;
    }

    public Long leftPushAllListObj(Object key, Collection<Object> values) {
        key = resolveObjKey(key);
        Long res = this.objList.leftPushAll(key, values);
        expireObjIfPressure(key);
        return res;
    }

    public Long leftPushIfPresentList(String key, String value) {
        key = resolveKey(key);
        Long res = this.strList.leftPushIfPresent(key, value);
        expireIfPressure(key);
        return res;
    }

    public Long leftPushIfPresentListObj(Object key, Object value) {
        key = resolveObjKey(key);
        Long res = this.objList.leftPushIfPresent(key, value);
        expireObjIfPressure(key);
        return res;
    }

    public String leftPopList(String key) {
        key = resolveKey(key);
        return this.strList.leftPop(key);
    }

    public Object leftPopListObj(Object key) {
        key = resolveObjKey(key);
        return this.objList.leftPop(key);
    }

    public String leftPopList(String key, long timeout, TimeUnit timeUnit) {
        key = resolveKey(key);
        return this.strList.leftPop(key, timeout, timeUnit);
    }

    public Object leftPopListObj(Object key, long timeout, TimeUnit timeUnit) {
        key = resolveObjKey(key);
        return this.objList.leftPop(key, timeout, timeUnit);
    }

    public Long rightPushList(String key, String value) {
        key = resolveKey(key);
        Long res = this.strList.rightPush(key, value);
        expireIfPressure(key);
        return res;
    }

    public Long rightPushListObj(Object key, Object value) {
        key = resolveObjKey(key);
        Long res = this.objList.rightPush(key, value);
        expireObjIfPressure(key);
        return res;
    }

    public Long rightPushList(String key, String pivot, String value) {
        key = resolveKey(key);
        Long res = this.strList.rightPush(key, pivot, value);
        expireIfPressure(key);
        return res;
    }

    public Long rightPushListObj(Object key, Object pivot, Object value) {
        key = resolveObjKey(key);
        Long res = this.objList.rightPush(key, pivot, value);
        expireObjIfPressure(key);
        return res;
    }

    public Long rightPushAllList(String key, String... vals) {
        key = resolveKey(key);
        Long res = this.strList.rightPushAll(key, vals);
        expireIfPressure(key);
        return res;
    }

    public Long rightPushAllListObj(Object key, Collection<Object> values) {
        key = resolveObjKey(key);
        Long res = this.objList.rightPushAll(key, values);
        expireObjIfPressure(key);
        return res;
    }

    public Long rightPushIfPresentList(String key, String value) {
        key = resolveKey(key);
        Long res = this.strList.rightPushIfPresent(key, value);
        expireIfPressure(key);
        return res;
    }

    public Long rightPushIfPresentListObj(Object key, Object value) {
        key = resolveObjKey(key);
        Long res = this.objList.rightPushIfPresent(key, value);
        expireObjIfPressure(key);
        return res;
    }

    public String rightPopList(String key) {
        key = resolveKey(key);
        return this.strList.rightPop(key);
    }

    public Object rightPopListObj(Object key) {
        key = resolveObjKey(key);
        return this.objList.rightPop(key);
    }

    public String rightPopList(String key, long timeout, TimeUnit timeUnit) {
        key = resolveKey(key);
        return this.strList.rightPop(key, timeout, timeUnit);
    }

    public Object rightPopListObj(Object key, long timeout, TimeUnit timeUnit) {
        key = resolveObjKey(key);
        return this.objList.rightPop(key, timeout, timeUnit);
    }

    public String rightPopAndLeftPushList(String sourceKey, String destinationKey) {
        sourceKey = resolveKey(sourceKey);
        destinationKey = resolveKey(destinationKey);
        return this.strList.rightPopAndLeftPush(sourceKey, destinationKey);
    }

    public Object rightPopAndLeftPushListObj(Object sourceKey, Object destinationKey) {
        sourceKey = resolveObjKey(sourceKey);
        destinationKey = resolveObjKey(destinationKey);
        return this.objList.rightPopAndLeftPush(sourceKey, destinationKey);
    }

    public List<String> rangeList(String key, long start, long end) {
        key = resolveKey(key);
        return this.strList.range(key, start, end);
    }

    public List<Object> rangeListObj(Object key, long start, long end) {
        key = resolveObjKey(key);
        return this.objList.range(key, start, end);
    }

    public Long removeList(String key, long count, Object value) {
        key = resolveKey(key);
        return this.strList.remove(key, count, value);
    }

    public Long removeListObj(Object key, long count, Object value) {
        key = resolveObjKey(key);
        return this.objList.remove(key, count, value);
    }

    public Long sizeList(String key) {
        key = resolveKey(key);
        return this.strList.size(key);
    }

    public Long sizeListObj(Object key) {
        key = resolveObjKey(key);
        return this.objList.size(key);
    }

    public void trimList(String key, long start, long end) {
        key = resolveKey(key);
        this.strList.trim(key, start, end);
    }

    public void trimListObj(Object key, long start, long end) {
        key = resolveObjKey(key);
        this.objList.trim(key, start, end);
    }

    public Boolean existKey(String key) {
        key = resolveKey(key);
        return strTemplate.opsForValue().getOperations().hasKey(key);
    }

    public Boolean existKeyObj(Object key) {
        key = resolveObjKey(key);
        return objTemplate.opsForValue().getOperations().hasKey(key);
    }

    public boolean existHashKey(String key, String field) {
        key = resolveKey(key);
        return strTemplate.opsForHash().hasKey(key, field);
    }

    public boolean existHashKeyObj(Object key, Object field) {
        key = resolveObjKey(key);
        return objTemplate.opsForHash().hasKey(key, field);
    }

    public void setHash(String key, String field, String val) {
        key = resolveKey(key);
        this.strHash.put(key, field, val);
        expireIfPressure(key);
    }

    public void setHash(String key, Map<String, String> vals) {
        final String finalKey = resolveKey(key);
        if (vals != null && !vals.isEmpty()) {
            vals.forEach((k, v) -> this.strHash.put(finalKey, k, v));
        }
        expireIfPressure(finalKey);
    }

    public void setHashObj(Object key, Object field, Object val) {
        key = resolveObjKey(key);
        this.objHash.put(key, field, val);
        expireObjIfPressure(key);
    }

    public void setHashObj(Object key, Map<Object, Object> vals) {
        final Object finalKey = resolveObjKey(key);
        if (vals != null && !vals.isEmpty()) {
            vals.forEach((k, v) -> this.objHash.put(finalKey, k, v));
        }
        expireObjIfPressure(finalKey);
    }

    public String getHash(String key, String field) {
        key = resolveKey(key);
        return this.strHash.get(key, field);
    }

    public <T> T getHashObj(Object key, Object field) {
        key = resolveObjKey(key);
        return (T) this.objHash.get(key, field);
    }

    public Map<String, String> getAllHash(String key) {
        key = resolveKey(key);
        return this.strHash.entries(key);
    }

    public Map<Object, Object> getAllHashObj(Object key) {
        key = resolveObjKey(key);
        return this.objHash.entries(key);
    }

    public Set<Object> getHashKeys(String key) {
        key = resolveKey(key);
        return this.strTemplate.opsForHash().keys(key);
    }

    public Set<Object> getHashKeysObj(Object key) {
        key = resolveObjKey(key);
        return this.objTemplate.opsForHash().keys(key);
    }

    public long getHashSize(String key) {
        key = resolveKey(key);
        return this.strTemplate.opsForHash().size(key);
    }

    public long getHashSizeObj(Object key) {
        key = resolveObjKey(key);
        return this.objTemplate.opsForHash().size(key);
    }

    public String getList(String key, int index) {
        key = resolveKey(key);
        return this.strList.index(key, index);
    }

    public Object getListObj(Object key, int index) {
        key = resolveObjKey(key);
        return this.objList.index(key, index);
    }

    public Long getListSize(String key) {
        key = resolveKey(key);
        return this.strList.size(key);
    }

    public List<String> getAllList(String key) {
        key = resolveKey(key);
        return this.strList.range(key, 0, getListSize(key));
    }

    public Long getListSizeObj(Object key) {
        key = resolveObjKey(key);
        return this.objList.size(key);
    }

    public List<Object> getAllListObj(Object key) {
        key = resolveObjKey(key);
        return this.objList.range(key, 0, getListSizeObj(key));
    }

    public String popList(String key) {
        key = resolveKey(key);
        return this.strList.leftPop(key);
    }

    public Object popListObj(Object key) {
        key = resolveObjKey(key);
        return this.objList.leftPop(key);
    }

    public Boolean del(String key) {
        key = resolveKey(key);
        return this.strTemplate.delete(key);
    }

    public Boolean delObj(Object key) {
        key = resolveObjKey(key);
        return this.objTemplate.delete(key);
    }

    public Long delHash(String key, Object... fields) {
        key = resolveKey(key);
        return this.strHash.delete(key, fields);
    }

    public Long delHashObj(Object key, Object... fields) {
        key = resolveObjKey(key);
        return this.objHash.delete(key, fields);
    }

    public Long removeList(String key, String val, int count) {
        key = resolveKey(key);
        return this.strList.remove(key, count, val);
    }

    public Long removeListObj(Object key, Object val, int count) {
        key = resolveObjKey(key);
        return this.objList.remove(key, count, val);
    }

    public void expire(String key, long expires) {
        expire(key, expires, TimeUnit.MILLISECONDS);
    }

    public void expireObj(Object key, long expires) {
        expireObj(key, expires, TimeUnit.MILLISECONDS);
    }

    public void expire(String key, long expires, TimeUnit timeUnit) {
        key = resolveKey(key);
        this.strTemplate.expire(key, expires, timeUnit);
    }

    public void expireObj(Object key, long expires, TimeUnit timeUnit) {
        key = resolveObjKey(key);
        this.objTemplate.expire(key, expires, timeUnit);
    }

    public Long incrHash(String key, String field, long delta) {
        key = resolveKey(key);
        return this.strTemplate.opsForHash().increment(key, field, delta);
    }

    public Object execute(SessionCallback<Object> callback) {
        return strTemplate.execute(callback);
    }

    public Object executeObj(SessionCallback<Object> callback) {
        return objTemplate.execute(callback);
    }

    public <T> T execute(RedisScript<T> script, List<String> keys, Object... args) {
        keys = resolveKeys(keys);
        return this.strTemplate.execute(script, keys, args);
    }

    public <T> T executeObj(RedisScript<T> script, List<Object> keys, Object... args) {
        keys = resolveObjKeys(keys);
        return this.objTemplate.execute(script, keys, args);
    }

    protected String resolveKey(String key) {
        return EnvContext.isPressureForRedis() ? PressureRedisKeyUtils.getKey(key) : key;
    }

    protected Object resolveObjKey(Object key) {
        if (key instanceof String) {
            return resolveKey((String) key);
        }
        return EnvContext.isPressureForRedis() ? PressureRedisKeyUtils.getObjKey(key) : key;
    }

    protected Map<String, String> resolveKeys(Map<String, String> map) {
        if (!EnvContext.isPressureForRedis()) {
            return map;
        }
        Map<String, String> newMap = new HashMap<>();
        map.forEach((k, v) -> newMap.put(PressureRedisKeyUtils.getKey(k), v));
        return newMap;
    }

    protected Map<Object, Object> resolveObjKeys(Map<Object, Object> map) {
        if (!EnvContext.isPressureForRedis()) {
            return map;
        }
        Map<Object, Object> newMap = new HashMap<>();
        map.forEach((k, v) -> {
            Object newK;
            if (k instanceof String) {
                newK = PressureRedisKeyUtils.getKey((String) k);
            } else {
                newK = PressureRedisKeyUtils.getObjKey(k);
            }
            newMap.put(newK, v);
        });
        return newMap;
    }

    protected List<String> resolveKeys(List<String> keys) {
        if (!EnvContext.isPressureForRedis()) {
            return keys;
        }
        List<String> newKeys = new ArrayList<>();
        for (int i = 0; i < keys.size(); i++) {
            newKeys.add(PressureRedisKeyUtils.getKey(keys.get(i)));
        }
        return newKeys;
    }

    protected List<Object> resolveObjKeys(List<Object> keys) {
        if (!EnvContext.isPressureForRedis()) {
            return keys;
        }
        List<Object> newKeys = new ArrayList<>();
        for (int i = 0; i < keys.size(); i++) {
            Object originKey = keys.get(i);
            Object newK;
            if (originKey instanceof String) {
                newK = PressureRedisKeyUtils.getKey((String) originKey);
            } else {
                newK = PressureRedisKeyUtils.getObjKey(originKey);
            }
            newKeys.add(newK);
        }
        return newKeys;
    }

    /**
     * 压测数据仅保留2小时
     * @param key key
     */
    protected void expireIfPressure(String key) {
        expireIfPressure(key, 0, TimeUnit.SECONDS);
    }

    /**
     * 压测数据仅保留2小时
     * @param keys keys
     */
    protected void expireIfPressures(Collection<String> keys) {
        if (EnvContext.isPressureForRedis() && keys != null) {
            keys.forEach(key -> expireIfPressure(key, 0, TimeUnit.SECONDS));
        }
    }

    /**
     * 压测数据仅保留2小时
     * @param keys keys
     * @param businessExpire 过期时间
     * @param timeUnit 时间单位
     */
    protected void expireIfPressures(Collection<String> keys, long businessExpire, TimeUnit timeUnit) {
        if (EnvContext.isPressureForRedis() && keys != null) {
            keys.forEach(key -> expireIfPressure(key, businessExpire, timeUnit));
        }
    }

    /**
     * 压测数据仅保留2小时
     * @param key key
     */
    protected void expireIfPressure(String key, long businessExpire, TimeUnit timeUnit) {
        if (EnvContext.isPressureForRedis()) {
            long timeout = EnvConfigBean.DEFAULT_PRESSURE_REDIS_EXPIRE;
            if (this.envConfigBean != null && this.envConfigBean.getPressureRedisExpire() != null) {
                timeout = this.envConfigBean.getPressureRedisExpire();
            }
            timeout = Math.max(1, timeout);
            if (businessExpire <= 0 || timeout < timeUnit.toSeconds(businessExpire)) {
                this.strTemplate.expire(key, timeout, TimeUnit.SECONDS);
            }
        }
    }

    /**
     * 压测数据仅保留2小时
     * @param key key
     */
    protected void expireObjIfPressure(Object key) {
        expireObjIfPressure(key, 0, TimeUnit.SECONDS);
    }

    /**
     * 压测数据仅保留2小时
     * @param keys keys
     */
    protected void expireObjIfPressures(Collection<Object> keys) {
        if (EnvContext.isPressureForRedis() && keys != null) {
            keys.forEach(key -> expireObjIfPressure(key, 0, TimeUnit.SECONDS));
        }
    }

    /**
     * 压测数据最多仅保留2小时
     * @param keys keys
     * @param businessExpire 过期时间
     * @param timeUnit 时间单位
     */
    protected void expireObjIfPressures(Collection<Object> keys, long businessExpire, TimeUnit timeUnit) {
        if (EnvContext.isPressureForRedis() && keys != null) {
            keys.forEach(key -> expireObjIfPressure(key, businessExpire, timeUnit));
        }
    }

    /**
     * 压测数据仅保留2小时
     * @param key key
     * @param businessExpire 业务指定的超时时间(为0表示未设置)
     */
    protected void expireObjIfPressure(Object key, long businessExpire, TimeUnit timeUnit) {
        if (EnvContext.isPressureForRedis()) {
            long timeout = EnvConfigBean.DEFAULT_PRESSURE_REDIS_EXPIRE;
            if (this.envConfigBean != null && this.envConfigBean.getPressureRedisExpire() != null) {
                timeout = this.envConfigBean.getPressureRedisExpire();
            }
            timeout = Math.max(1, timeout);
            if (businessExpire <= 0 || timeout < timeUnit.toSeconds(businessExpire)) {
                this.objTemplate.expire(key, timeout, TimeUnit.SECONDS);
            }
        }
    }

}
